// Thud - a dynamic object-oriented language
// Copyright (C) 2009,2010 David H. Hovemeyer <david.hovemeyer@gmail.com>

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

// String utility functions

#include <cassert>
#include <cstring>
#include <memory>
#include "util/StringOutputStream.h"
#include "util/OutputStreamWriter.h"
#include "util/StringInputStream.h"
#include "util/InputStreamReader.h"
#include "util/String.h"

char *String::dup(const char *s)
{
	char *copy = new char[strlen(s) + 1];
	strcpy(copy, s);
	return copy;
}

void String::destroy(char *s)
{
	delete[] s;
}

wchar_t *String::wdup(const wchar_t *s)
{
	wchar_t *copy = new wchar_t[wcslen(s) + 1];
	wcscpy(copy, s);
	return copy;
}

void String::wdestroy(wchar_t *s)
{
	delete[] s;
}

std::wstring String::asWString(int32_t i)
{
	wchar_t buf[50];
	swprintf(buf, sizeof(buf), L"%d", i);
	return std::wstring(buf);
}

int String::wsToInt(const wchar_t *s)
{
	bool neg = false;
	if (*s == L'-') {
		neg = true;
		s++;
	}

	long lval = 0;
	while (*s != L'\0') {
		lval *= 10;
		lval += (*s - L'0');
		s++;
	}

	if (neg) {
		lval = -lval;
	}

	return int(lval);
}

bool String::endsWith(const std::string &s, const std::string &suffix)
{
	size_t i = s.rfind(suffix);
	if (i == std::string::npos) {
		return false;
	}
	return i + suffix.size() == s.size();
}

std::string String::toUtf8(const std::wstring &ws)
{
	std::string s;

	std::auto_ptr<OutputStreamWriter> writer(new OutputStreamWriter(new StringOutputStream(s)));
	writer->write(ws.c_str(), ws.size());

	return s;
}

std::wstring String::fromUtf8(const std::string &s)
{
	std::wstring ws;

	std::auto_ptr<InputStreamReader> reader(new InputStreamReader(new StringInputStream(s)));
	while (true) {
		long c = reader->readChar();
		if (c < 0) {
			break;
		}
		ws += wchar_t(c);
	}
	return ws;
}

// This code shamelessly stolen from
// http://www.gammon.com.au/forum/?id=2891
std::string String::replace(const std::string &source, const std::string &target, const std::string &replacement)
{
	std::string str = source;
	std::string::size_type pos = 0,   // where we are now
			found;     // where the found data is

	if (target.size () > 0)   // searching for nothing will cause a loop
	{
		while ((found = str.find (target, pos)) != std::string::npos)
		{
			str.replace (found, target.size (), replacement);
			pos = found + replacement.size ();
		}
	}

	return str;
}

// This code shamelessly stolen from
// http://oopweb.com/CPP/Documents/CPPHOWTO/Volume/C++Programming-HOWTO-7.html
void String::tokenize(const std::string& str,
		std::vector<std::string>& tokens,
		const std::string& delimiters)
{
	// Skip delimiters at beginning.
	std::string::size_type lastPos = str.find_first_not_of(delimiters, 0);
	// Find first "non-delimiter".
	std::string::size_type pos     = str.find_first_of(delimiters, lastPos);

	while (std::string::npos != pos || std::string::npos != lastPos)
	{
		// Found a token, add it to the vector.
		tokens.push_back(str.substr(lastPos, pos - lastPos));
		// Skip delimiters.  Note the "not_of"
		lastPos = str.find_first_not_of(delimiters, pos);
		// Find next "non-delimiter"
		pos = str.find_first_of(delimiters, lastPos);
	}
}

void String::split(const std::string &str, std::vector<std::string> &tokens, const std::string &sep)
{
	std::string::size_type pos = 0;

	std::string::size_type sepPos = str.find(sep);
	if (sepPos == std::string::npos) {
		// no separators
		tokens.push_back(str);
		return;
	}

	// push first token
	tokens.push_back(str.substr(pos, sepPos));

	// extract remaining tokens, each of which follows a separator
	pos = sepPos + sep.size();
	while (true) {
		// find next separator (following the current token)
		sepPos = str.find(sep, pos);

		// no more separators?
		if (sepPos == std::string::npos) {
			// extract token at end of string, and we're done
			tokens.push_back(str.substr(pos));
			break;
		}

		// extract token
		tokens.push_back(str.substr(pos, sepPos));

		// move position to just past the separator
		pos = sepPos + sep.size();
		assert(pos <= str.size());
	}
}

void String::join(const std::vector<std::string> &tokens, std::string &str, const std::string &sep)
{
	for (std::vector<std::string>::const_iterator i = tokens.begin(); i != tokens.end(); i++) {
		if (i != tokens.begin()) {
			str += sep;
		}
		str += *i;
	}
}
